#include "stdafx.h"
#include "Utility.hpp"

#include "Namespaces.h"

using namespace System::Linq::Expressions;
using namespace System::Reflection;
using namespace System::Reflection::Emit;

static Utility::Utility(void) {

	/* Load GetPrivateResourceString function */ {

		auto TypeInfo = PartModule::typeid->Assembly->GetType("A.\b\a");
		auto MethodInfo = TypeInfo->GetMethod("\2", BindingFlags::NonPublic | BindingFlags::Static);
		auto IDParam = Expression::Parameter(int::typeid, "ID");
		auto FuncCallExpr = Expression::Call(IDParam, MethodInfo);
		auto LambdaExpr = Expression::Lambda(FuncCallExpr, IDParam);
		
		Utility::s_GetPrivateResourceStringFn = dynamic_cast<Func<int, String ^> ^>(LambdaExpr->Compile());

	}

}

generic<class CT>
Action<CT> ^Utility::GeneratePrivateMethodDelegate(String ^Method) {

	auto MethodInfo = OrbitDriver::typeid->GetMethod(Method, BindingFlags::NonPublic | BindingFlags::Instance);
	auto ThisParam = Expression::Parameter(CT::typeid, "this");
	auto FuncCallExpr = Expression::Call(ThisParam, MethodInfo); 
	auto LambdaExpr = Expression::Lambda(FuncCallExpr, ThisParam);
	return dynamic_cast<Expression<Action<CT> ^> ^>(LambdaExpr)->Compile();

}

generic<class CT, typename FT>
Func<CT, FT> ^Utility::GeneratePrivateFieldGetDelegate(String ^Field) {

	auto FieldInfo = OrbitDriver::typeid->GetField(Field, BindingFlags::NonPublic | BindingFlags::Instance);
	auto ThisParam = Expression::Parameter(CT::typeid, "this");
	auto FieldAccessExpr = Expression::Field(ThisParam, FieldInfo);
	auto LambdaExpr = Expression::Lambda(FieldAccessExpr, ThisParam);
	return dynamic_cast<Expression<Func<CT, FT> ^> ^>(LambdaExpr)->Compile();

}

generic<class CT, typename FT>
Action<CT, FT> ^Utility::GeneratePrivateFieldSetDelegate(String ^Field) {

	auto FieldInfo = OrbitDriver::typeid->GetField(Field, BindingFlags::NonPublic | BindingFlags::Instance);
	auto Ret = gcnew DynamicMethod(String::Empty, FT::typeid, gcnew array<Type ^> { CT::typeid }, true);
	auto Gen = Ret->GetILGenerator();
	Gen->Emit(OpCodes::Ldarg_0);
	Gen->Emit(OpCodes::Ldarg_1);
	Gen->Emit(OpCodes::Stfld, FieldInfo);
	Gen->Emit(OpCodes::Ret);
	return dynamic_cast<Action<CT, FT> ^>(Ret->CreateDelegate(Action<CT, FT>::typeid));

}

String ^Utility::GetPrivateResourceString(int ID) {

#if _DEBUG

	auto Str = Utility::s_GetPrivateResourceStringFn->Invoke(ID);
	Debug_LogF("PrivateResourceString({0}) => \"{1}\"", ID, Str);
	return Str;

#else

	return Utility::s_GetPrivateResourceStringFn->Invoke(ID);

#endif

}